﻿using System;
using System.Collections.Generic;
using System.Linq;

namespace g4
{
    /// <summary>
    /// 网格面挤出操作类
    /// 功能：对网格中选定的一组面进行挤出操作，生成新的几何结构
    /// 操作步骤：
    /// 1) 将选定的面集与相邻三角形分离
    /// 2) 偏移选定的面集到新位置
    /// 3) 用三角形条带连接原始面集和偏移面集的边界边
    /// 
    /// 注意事项：
    ///    - 不确定是否支持多个分离区域的挤出
    ///    - 当前边界顶点会直接附加到偏移区域，而非复制后再用条带连接
    ///      [待实现] 未来将优化此行为
    /// </summary>
    public class MeshExtrudeFaces
    {
        /// <summary>
        /// 要操作的目标网格
        /// </summary>
        public DMesh3 Mesh;

        /// <summary>
        /// 要挤出的三角形ID数组
        /// </summary>
        public int[] Triangles;

        /// <summary>
        /// 新生成三角形的分组行为（自动生成或指定组ID）
        /// </summary>
        public SetGroupBehavior Group = SetGroupBehavior.AutoGenerate;

        /// <summary>
        /// 计算挤出后顶点位置的函数
        /// 输入：原始顶点位置、原始顶点法向量、顶点ID
        /// 输出：挤出后新顶点的位置
        /// </summary>
        public Func<Vector3d, Vector3f, int, Vector3d> ExtrudedPositionF;

        // 操作结果

        /// <summary>
        /// 边对列表（原始边与挤出边的对应关系），用于后续缝合
        /// </summary>
        public List<Index2i> EdgePairs;

        /// <summary>
        /// 挤出区域的顶点选择集（标记所有参与挤出的顶点）
        /// </summary>
        public MeshVertexSelection ExtrudeVertices;

        /// <summary>
        /// 用于连接原始面和挤出面边界的三角形ID数组
        /// 若JoinIncomplete为true，可能包含无效三角形ID
        /// </summary>
        public int[] JoinTriangles;

        /// <summary>
        /// 连接三角形所属的组ID
        /// </summary>
        public int JoinGroupID;

        /// <summary>
        /// 标记缝合操作是否存在不完整（如遇到错误或无法缝合的边）
        /// </summary>
        public bool JoinIncomplete = false;


        /// <summary>
        /// 构造函数：初始化面挤出操作
        /// </summary>
        /// <param name="mesh">目标网格</param>
        /// <param name="triangles">要挤出的三角形ID数组</param>
        /// <param name="bForceCopyArray">是否强制复制三角形数组（避免外部修改影响内部）</param>
        public MeshExtrudeFaces(DMesh3 mesh, int[] triangles, bool bForceCopyArray = false)
        {
            Mesh = mesh;
            // 根据参数决定是否复制三角形数组
            if (bForceCopyArray)
                Triangles = (int[])triangles.Clone();
            else
                Triangles = triangles;

            // 默认挤出位置函数：沿Y轴正方向挤出
            ExtrudedPositionF = (pos, normal, idx) => {
                return pos + Vector3d.AxisY;
            };
        }

        /// <summary>
        /// 构造函数：从三角形ID枚举初始化面挤出操作
        /// </summary>
        /// <param name="mesh">目标网格</param>
        /// <param name="triangles">要挤出的三角形ID枚举</param>
        public MeshExtrudeFaces(DMesh3 mesh, IEnumerable<int> triangles)
        {
            Mesh = mesh;
            Triangles = triangles.ToArray();

            // 默认挤出位置函数：沿Y轴正方向挤出
            ExtrudedPositionF = (pos, normal, idx) => {
                return pos + Vector3d.AxisY;
            };
        }


        /// <summary>
        /// 验证操作的合法性
        /// </summary>
        /// <returns>验证状态（当前始终返回Ok，待实现边界检查）</returns>
        public virtual ValidationStatus Validate()
        {
            // [待实现] 检查边界是否有效

            return ValidationStatus.Ok;
        }


        /// <summary>
        /// 执行面挤出操作
        /// 若返回false，说明操作未完成，但网格可能已被修改（可能存在孔洞）
        /// </summary>
        /// <returns>操作是否完全成功（无错误且缝合完整）</returns>
        public virtual bool Extrude()
        {
            MeshEditor editor = new MeshEditor(Mesh);

            // 1. 将选定三角形与相邻三角形分离，获取边界边对
            // 分离后，选定三角形的边界将成为新的网格边界
            bool isSeparateOK = editor.SeparateTriangles(Triangles, true, out EdgePairs);

            // 准备顶点法向量（若网格没有预计算的法向量，则临时计算）
            MeshNormals normals = null;
            bool hasNormals = Mesh.HasVertexNormals;
            if (!hasNormals)
            {
                normals = new MeshNormals(Mesh);
                normals.Compute();
            }

            // 2. 标记并偏移挤出区域的顶点
            ExtrudeVertices = new MeshVertexSelection(Mesh);
            ExtrudeVertices.SelectTriangleVertices(Triangles); // 选择所有属于挤出面的顶点

            // 计算所有挤出顶点的新位置
            Vector3d[] newVertexPositions = new Vector3d[ExtrudeVertices.Count];
            int index = 0;
            foreach (int vid in ExtrudeVertices)
            {
                Vector3d originalPos = Mesh.GetVertex(vid); // 原始顶点位置
                // 获取顶点法向量（使用预计算的或临时计算的）
                Vector3f normal = hasNormals ?
                    Mesh.GetVertexNormal(vid) :
                    (Vector3f)normals.Normals[vid];
                // 调用位置函数计算挤出后的位置
                newVertexPositions[index++] = ExtrudedPositionF(originalPos, normal, vid);
            }

            // 更新顶点位置到新的挤出位置
            index = 0;
            foreach (int vid in ExtrudeVertices)
            {
                Mesh.SetVertex(vid, newVertexPositions[index++]);
            }

            // 3. 缝合原始边界与挤出后的边界，生成连接三角形
            JoinGroupID = Group.GetGroupID(Mesh); // 获取连接三角形的组ID
            // 缝合边对，生成连接条带
            JoinTriangles = editor.StitchUnorderedEdges(
                EdgePairs, JoinGroupID, false, out JoinIncomplete);

            // 返回操作是否完全成功（缝合完成且无错误）
            return JoinTriangles != null && JoinIncomplete == false;
        }
    }
}